using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Collections.ObjectModel;

namespace Decav.Data
{
    /// <summary>
    /// Reads <see cref="DataRecordAdapter"/> objects from database records.
    /// </summary>
    public class DataRecordAdapterReader : IDisposable
    {
        /// <summary>
        /// Creates a new reader.
        /// </summary>
        /// <param name="command">The SQL command executed to get the reader.</param>
        /// <param name="reader">The reader to read from.</param>
        public DataRecordAdapterReader(IDataReader reader)
        {        
            if ( reader == null )
                throw new ArgumentNullException( "reader" );
        
            _reader = reader;
        }

        private IDataReader _reader;
        private DataRecordAdapter _current;
        private bool _lastOutcome;
        private bool _hasRead;

        /// <summary>
        /// Reads to the next <see cref="DataRecordAdapter"/>.
        /// </summary>
        /// <returns>True if another read occured, false if at the end.</returns>
        public bool Read()
        {
            _hasRead = true;
            _current = null;
            _lastOutcome = _reader.Read();
            return _lastOutcome;
        }

        /// <summary>
        /// Gets the current <see cref="DataRecordAdapter"/> iterated to.
        /// </summary>
        public DataRecordAdapter Current
        {
            get
            {
                if (_lastOutcome)
                {
                    if (_current == null)
                        _current = new DataRecordAdapter((IDataRecord)_reader);

                    return _current;
                }
                else
                {
                    throw new InvalidOperationException("Cannot read the current item in the reader because Read has not yet been invoked, or because the reader has read to the end of the records.");
                }
            }
        }

        /// <summary>
        /// Creates an instance of a type given the <see cref="Current"/> adapter.
        /// </summary>
        /// <typeparam name="TType">The type to create.</typeparam>
        /// <returns>The newly created, fully constructed instance.</returns>
        public TType CreateInstance<TType>()
        {
            return DataRecordAdapter.CreateInstance<TType>(Current);
        }

        /// <summary>
        /// Creates a collection of all instances of the items in the reader.
        /// </summary>
        /// <typeparam name="TType">The type of object to create.</typeparam>
        /// <returns>A collection of all items read from the reader.</returns>
        public ReadOnlyCollection<TType> CreateAllInstances<TType>()
        {
            if (_hasRead)
                throw new InvalidOperationException("Cannot create instances of all items in the adapter after reading has started.");


            Collection<TType> items = new Collection<TType>();
            while (this.Read())
                items.Add(CreateInstance<TType>());

            return new ReadOnlyCollection<TType>(items);
        }

        #region IDisposable Members

        /// <summary>
        /// Disposes the reader.
        /// </summary>
        public void  Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Disposes the reader.
        /// </summary>
        /// <param name="disposing">True when <see cref="Dispose"/> is explicitly invoked.</param>
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (_reader != null)
                    _reader.Dispose();
            }
        }

        ~DataRecordAdapterReader()
        {
            Dispose(false);
        }

        #endregion
    }
}
